###########################################################################
#
#   Copyright 2010 American Public Media Group
#
#   This file is part of AIR2.
#
#   AIR2 is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   AIR2 is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################

package AIR2::Searcher::Sources::Engine;
use strict;
use warnings;
use base qw( AIR2::Searcher::Engine );
use Carp;
use Data::Dump qw( dump );

my @summary_fields = qw(
    job_title
    employer
    political_office
    birth_year
    education_level
    ethnicity
    gender
    household_income
    lifecycle
    political_affiliation
    religion
    source_website
    interest
    experience
);

# TODO
my %multival_fields = map { $_ => $_ } qw(
    src_uuid
    qa
    response
    tag
    org_name
    org_uuid
    so_org_id
    org_status
    inq_uuid_title
    inq_title
    inq_ext_title
    prj_title
    prj_uuid
);

my %collapse_dupe_fields = map { $_ => $_ } qw(
    org_name
    org_uuid
    so_org_id
    inq_uuid_title
    inq_title
    inq_ext_title
    prj_title
    prj_uuid
    src_username
    tag
);

sub search {
    my $self = shift;
    my $resp = $self->SUPER::search(@_);

    # not a real field
    push @{ $resp->metaData->{fields} }, 'excerpts';

    return $resp;
}

sub _summarize_last_contacted_date {
    my ( $self, $facets ) = @_;
    return $self->_do_date_range_summary($facets);
}

sub _summarize_last_response_date {
    my ( $self, $facets ) = @_;
    return $self->_do_date_range_summary($facets);
}

sub _summarize_first_response_date {
    my ( $self, $facets ) = @_;
    return $self->_do_date_range_summary($facets);
}

sub _summarize_last_activity_date {
    my ( $self, $facets ) = @_;
    return $self->_do_date_range_summary($facets);
}

sub _summarize_birth_year {
    my ( $self, $facets ) = @_;

    my $this_year = (localtime)[5] + 1900;
    my %spans;
    my %terms = (
        "0-18" => sprintf( "(%d..%d)", ( $this_year - 18 ), $this_year ),
        "19-24" =>
            sprintf( "(%d..%d)", ( $this_year - 24 ), ( $this_year - 19 ) ),
        "25-34" =>
            sprintf( "(%d..%d)", ( $this_year - 34 ), ( $this_year - 25 ) ),
        "35-44" =>
            sprintf( "(%d..%d)", ( $this_year - 44 ), ( $this_year - 35 ) ),
        "45-54" =>
            sprintf( "(%d..%d)", ( $this_year - 54 ), ( $this_year - 45 ) ),
        "55-64" =>
            sprintf( "(%d..%d)", ( $this_year - 64 ), ( $this_year - 55 ) ),
        "65+" => sprintf( "(%d..%d)", 1900, ( $this_year - 65 ) ),
    );
    for my $f (@$facets) {
        my $year = $f->{term};
        my $c    = $f->{count};

        # skip any garbage
        if ( !$year or $year =~ m/\D/ or $year > $this_year or $year < 1900 )
        {
            $spans{"invalid"} += $c;
            next;
        }

        my $age = $this_year - $year;

        if ( $age < 19 ) {
            $spans{"0-18"} += $c;
        }
        elsif ( $age < 25 ) {
            $spans{"19-24"} += $c;
        }
        elsif ( $age < 35 ) {
            $spans{"25-34"} += $c;
        }
        elsif ( $age < 45 ) {
            $spans{"35-44"} += $c;
        }
        elsif ( $age < 55 ) {
            $spans{"45-54"} += $c;
        }
        elsif ( $age < 65 ) {
            $spans{"55-64"} += $c;
        }
        else {
            $spans{"65+"} += $c;
        }
    }

    # put it back in the API format
    my @ages;
    for my $span ( keys %spans ) {
        push @ages,
            {
            term  => $terms{$span},
            label => $span,
            count => $spans{$span}
            };
    }
    return \@ages;
}

sub process_result {
    my ( $self, %args ) = @_;
    my $result       = $args{result};
    my $hiliter      = $args{hiliter};
    my $XMLer        = $args{XMLer};
    my $snipper      = $args{snipper};
    my $fields       = $args{fields};
    my $apply_hilite = $args{apply_hilite};
    my $org_masks    = $args{args}->{args}->{authz};

    my %res = (
        score => $result->score,
        uri   => $result->uri,
        mtime => $result->mtime,
    );

    $self->_fetch_prop_values( $result, \%res, $fields, \%multival_fields,
        \%collapse_dupe_fields, $XMLer );

    # hand-craft some values
    $res{uri}      = $res{src_uuid}->[0];
    $res{src_uuid} = $res{uri};             # yes, un-array-ify it
    $res{title}    = join( ', ',
        ( $res{src_last_name}  || '[last name]' ),
        ( $res{src_first_name} || '[first name]' ) );

    # use aliases
    # TODO $res{credential} = delete $res{sc_value} || '';
    # TODO $res{specialty}  = delete $res{ss_value} || '';

    $res{primary_location} = join( ', ',
        grep { defined and length }
            ( $res{primary_city}, $res{primary_state}, $res{primary_zip} ) );

    # build the summary
    $self->_build_summary(
        swish_result => $result,
        snipper      => $snipper,
        result       => \%res,
        authz        => $org_masks,
        hiliter      => ( $apply_hilite ? $hiliter : 0 ),
    );

    # finally, apply hiliting
    if ($apply_hilite) {
        for my $f (
            qw(
            summary
            title
            primary_location
            primary_email_html
            )
            )
        {
            $res{$f} = $hiliter->light( $res{$f} );
        }
    }

    # qa is a little tricky. only return based on authz,
    # and have to split out responses for highlighting.
    my @qa_ok;

    for my $qa ( @{ $res{qa} } ) {
        my ( $org_ids, $inq_uuid, $seq, $date, $resp )
            = (
            $qa =~ m/^(.+?):(\w+):(\d+):(\d+-\d+-\d+ \d+:\d+\d+):(.*)$/s );
        if ( !defined($org_masks)
            or $self->_has_authz( $org_masks, $org_ids ) )
        {
            if ($apply_hilite) {
                $qa = join( ':',
                    $org_ids, $inq_uuid, $seq, $date,
                    $hiliter->light($resp) );
            }
            push @qa_ok, $qa;
        }
    }

    $res{qa} = \@qa_ok;

    return \%res;
}

sub _build_summary {
    my ( $self, %args ) = @_;
    my $swish_result = $args{swish_result};
    my $snipper      = $args{snipper};
    my $result       = $args{result};
    my $hiliter      = $args{hiliter};
    my $org_masks    = $args{authz};
    my $query        = $snipper->query;

    my @responses;

    # authz implementation.
    # should not be able to see snippets from responses
    # to which requester does not have access.
    if ( exists $result->{qa} ) {
        for ( @{ $result->{qa} } ) {
            my ( $org_ids, $inq_uuid, $seq, $date, $resp )
                = (m/^(.+?):(\w+):(\d+):(.+?):(.+)/);

            if ( !defined($org_masks)
                or $self->_has_authz( $org_masks, $org_ids ) )
            {
                push @responses, $resp;
            }
        }
    }

    my @summary;
    my @excerpts;

RESPONSE: for my $r (@responses) {
        next unless defined $r and length $r;
        my $snip = $snipper->snip($r);
        if ($snip) {
            push @summary, $snip;
        }
    }

    # summary fields are not snipped, since the assumption is that they
    # are (a) short and (b) all relevant.
FIELD: for my $f (@summary_fields) {

        # already fetched
        if ( exists $result->{$f} ) {
            if ( ref $result->{$f} ) {
                for my $v ( @{ $result->{$f} } ) {
                    if ( $query->matches_text($v) ) {
                        push @excerpts,
                            {
                            field => $f,
                            snip  => $hiliter ? $hiliter->light($v) : $v
                            };
                        next FIELD;
                    }
                }
            }
            elsif ( $query->matches_text( $result->{$f} ) ) {
                push @excerpts,
                    {
                    field => $f,
                    snip  => $hiliter
                    ? $hiliter->light( $result->{$f} )
                    : $result->{$f}
                    };
                next FIELD;
            }
        }

        # need to fetch
        else {
            my $prop;
            eval { $prop = $swish_result->get_property($f); };
            if ($prop) {
                for my $v ( split( m/\003/, $prop ) ) {
                    if ( $query->matches_text($v) ) {
                        push @excerpts,
                            {
                            field => $f,
                            snip  => ( $hiliter ? $hiliter->light($v) : $v ),
                            };
                        next FIELD;
                    }
                }
            }
        }
    }

    $result->{excerpts} = \@excerpts;

    return $self->_cleanup_summary( $result, @summary );
}

sub get_uuids_only {
    my $self = shift;
    my %args = @_;
    $args{uuid_field} = 'src_uuid';
    return $self->SUPER::get_uuids_only(%args);
}

1;
